package net.danielthompson.danray.structures;

/**
 * DanRay
 * User: dthompson
 * Date: 6/27/13
 * Time: 1:49 PM
 */
public class Point {
   Tuple _tuple;

   public Point(double X, double Y, double Z) {
      _tuple = new Tuple(X, Y, Z);
   }

   public Point(Tuple tuple) {
      _tuple = tuple;
   }

   public double getX() {
      return _tuple.getX();
   }

   public double getY() {
      return _tuple.getY();
   }

   public double getZ() {
      return _tuple.getZ();
   }

   public Vector CreateVectorFrom(Point originPoint) {
      double xDirection = _tuple.getX() - originPoint._tuple.getX();
      double yDirection = _tuple.getY() - originPoint._tuple.getY();
      double zDirection = _tuple.getZ() - originPoint._tuple.getZ();

      Point direction = new Point(xDirection, yDirection, zDirection);
      return new Vector(originPoint, direction);
   }

   public double Dot(Point point) {
      return _tuple.Dot(point._tuple);
   }

   public Point Cross(Point point) {
      return new Point(_tuple.Cross(point._tuple));
   }

   public void Minus(Point point) {
      _tuple.Minus(point._tuple);
   }

   public static Point Minus(Point point1, Point point2) {
      return new Point(Tuple.Minus(point1._tuple, point2._tuple));
   }

   public double SquaredDistanceBetween(Point point) {
      return (point._tuple.getX() - _tuple.getX()) * (point._tuple.getX() - _tuple.getX()) +
             (point._tuple.getY() - _tuple.getY()) * (point._tuple.getY() - _tuple.getY()) +
             (point._tuple.getZ() - _tuple.getZ()) * (point._tuple.getZ() - _tuple.getZ());
   }

   public void Plus(Point point) {
      _tuple.Plus(point._tuple);
   }

   public static Point Plus(Point point1, Point point2) {
      return new Point(Tuple.Plus(point1._tuple, point2._tuple));
   }

   public void Normalize() {
      _tuple.Normalize();
   }

   public static Point Normalize(Point point) {
      return new Point(Tuple.Normalize(point._tuple));
   }

   public void Scale(double t) {
      _tuple.Scale(t);
   }

   public static Point Scale(Point point, double t) {
      return new Point(Tuple.Scale(point._tuple, t));
   }

   public double Length() {
      return _tuple.Length();
   }

   public String toString() {
      return _tuple.toString();
   }

   public boolean equals(Object obj) {
      if (obj == null)
         return false;
      if (obj == this)
         return true;
      if (!(obj instanceof Point))
         return false;

      Point rhs = (Point) obj;

      return (_tuple.equals(rhs._tuple));
   }

   public void Rotate(Vector axis, double theta) {
      theta = Math.toRadians(theta);

      double sinTheta = Math.sin(theta);
      double cosTheta = Math.cos(theta);

      double a = axis.Origin.getX();
      double b = axis.Origin.getY();
      double c = axis.Origin.getZ();

      double u = axis.Direction.getX();
      double v = axis.Direction.getY();
      double w = axis.Direction.getZ();

      double x = _tuple.getX();
      double y = _tuple.getY();
      double z = _tuple.getZ();

      double a1 = u * x - v * y - w * z;

      double newX = (a * (v * v + w * w) - u * (b * v + c * w - a1)) * (1 - cosTheta) + x * cosTheta + (-c * v + b * w - w * y + v * z) * sinTheta;
      double newY = (b * (u * u + w * w) - v * (a * u + c * w - a1)) * (1 - cosTheta) + y * cosTheta + (c * u - a * w + w * x - u * z) * sinTheta;
      double newZ = (c * (u * u + v * v) - w * (a * u + b * v - a1)) * (1 - cosTheta) + z * cosTheta + (-b * u + a * v - v * x + u * y) * sinTheta;

      _tuple.setX(newX);
      _tuple.setY(newY);
      _tuple.setZ(newZ);
   }

   public static Point Rotate(Point point, Vector axis, double theta) {

      theta = Math.toRadians(theta);

      double sinTheta = Math.sin(theta);
      double cosTheta = Math.cos(theta);

      double a = axis.Origin.getX();
      double b = axis.Origin.getY();
      double c = axis.Origin.getZ();

      double u = axis.Direction.getX();
      double v = axis.Direction.getY();
      double w = axis.Direction.getZ();

      double x = point.getX();
      double y = point.getY();
      double z = point.getZ();

      double a1 = u * x - v * y - w * z;

      double newX = (a * (v * v + w * w) - u * (b * v + c * w - a1)) * (1 - cosTheta) + x * cosTheta + (-c * v + b * w - w * y + v * z) * sinTheta;
      double newY = (b * (u * u + w * w) - v * (a * u + c * w - a1)) * (1 - cosTheta) + y * cosTheta + (c * u - a * w + w * x - u * z) * sinTheta;
      double newZ = (c * (u * u + v * v) - w * (a * u + b * v - a1)) * (1 - cosTheta) + z * cosTheta + (-b * u + a * v - v * x + u * y) * sinTheta;

      return new Point(newX, newY, newZ);
   }
}